
let Vue
let forEach = (obj, callback) => {
  Object.keys(obj).forEach(key => {
    callback(key, obj[key])
  })
}
// forEach({a:12,b:22},(key, val)=>{
//   console.log(key, val)
// })
class ModuleCollection {
  constructor(options) {
    this.register([], options)
  }
  register(path, rootModule) {
    // debugger
    let rawModule = {
      _raw: rootModule,
      state: rootModule.state,
      _children: {}
    }
    if (!this.root) {
      this.root = rawModule
    } else {
      /* // 自己实现的父级模块查找
      let currentModule = this.root
      if (path.length>=2){
        for(let i=0;i<path.length-1;i++) {
          currentModule = currentModule._children[path[i]]
        }
      }
      currentModule._children[path[path.length-1]] = rawModule
      */

      let parentModule = path.slice(0, -1).reduce((root, current) => {
        return root._children[current]
      }, this.root)
      parentModule._children[path[path.length - 1]] = rawModule
    }
    if (rootModule.modules) {
      forEach(rootModule.modules, (moduleName, module) => {
        this.register(path.concat(moduleName), module)
      })
    }
  }
}

function installModule(store, rootState, path, rawModule) {
  // 没有安装我们的状态  需要把子模块状态定义到rootState上
  if (path.length>0){
    Vue.set(rootState, path[path.length-1], rawModule._raw.state)
  }

  const getters = rawModule._raw.getters
  if (getters) {
    forEach(getters, (getterName, value) => {
      Object.defineProperty(store.getters, getterName, {
        get() {
          return value(rawModule._raw.state)
        }
      })
    })
  }
  const mutations = rawModule._raw.mutations
  if (mutations) {
    forEach(mutations, (mutationName, value) => {
      let arr = store.mutations[mutationName] || (store.mutations[mutationName] = [])
      arr.push((payload) => {
        return value(rawModule._raw.state, payload)
      })
    })
  }

  const actions = rawModule._raw.actions
  if (actions) {
    forEach(actions, (actionName, value) => {
      let arr = store.actions[actionName] || (store.actions[actionName] = [])
      arr.push((payload) => {
        value(store, payload)
      })
    })
  }

  if (rawModule._children) {
    forEach(rawModule._children, (moduleName, module) => {
      installModule(store, rootState, path.concat(moduleName), module)
    })
  }
}
class Store {
  constructor(options) {
    // this.state = options.state
    this.vm = new Vue({
      data: {
        state: options.state
      }
    })

    this.getters = {}
    this.mutations = {}
    this.actions = {}

    // 1、需要将用户传入的数据进行格式化操作
    this.modules = new ModuleCollection(options)
    // console.log(this.modules)
    // 递归安装模块 store/rootState/path/根模块
    installModule(this, this.state, [], this.modules.root)

    /*
    let getters = options.getters
    this.getters = {}
    forEach(getters, (getterName, value) => {
      Object.defineProperty(this.getters, getterName, {
        get: () => {
          return value(this.state)
        }
      })
    })
    // 需要将用户定义的mutation放到store上  订阅 将函数订阅到一个数组中 发布 让数组中的函数依次执行
    let mutations = options.mutations
    this.mutations = {}
    forEach(mutations, (mutationName, value) => {
      // this.mutations[mutationName] = value.bind(this, this.state) // 自己最开始写的
      this.mutations[mutationName] = (payload) => {
        value(this.state, payload)
      }
    })

    let actions = options.actions
    this.actions = {}
    forEach(actions, (actionName, value) => {
      // this.actions[actionName] = value.bind(this, this)// 自己最开始写的
      this.actions[actionName] = (payload) => {
        value(this, payload)
      }
    })
    */
  }
  commit = (mutationName, payload) => {
    // this.mutations[mutationName](payload)
    this.mutations[mutationName].forEach(fn => fn(payload))
  }
  dispatch = (actionName, payload) => {
    // this.actions[actionName](payload)
    this.actions[actionName].forEach(fn => fn(payload))
  }
  get state() {
    return this.vm.state
  }
}

const install = function (_Vue) {
  Vue = _Vue
  // 为什么不挂载到vue.prototype上去啊？  因为可能会有多个vue实例 但是我们只想让一个vue实例可以用store
  Vue.mixin({ // 组件的创建过程 先父后子 
    beforeCreate() {
      // if (this.)
      // console.log(this)
      if (this.$options.store) {
        this.$store = this.$options.store
      } else if (this.$options.parent && this.$options.parent.$store) {
        this.$store = this.$options.parent.$store
      }
    }
  })
}

export default {
  Store,
  install
}